from sys import argv
from math import sin, cos, radians
from numpy import identity, array
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

from GMI import *

class MyPyOpenGLTest:
    def __init__(self, width=640, height=480,
                 title='PyOpenGL二维变换'.encode('gbk')):
        '''初始化OpenGL环境，指定显示模式以及用于绘图的函数'''
        glutInit(argv)
        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
        # 设置图形窗口初始大小和位置，然后创建图形窗口，顺序不能变
        glutInitWindowSize(width, height)
        glutInitWindowPosition(400, 200)
        glutCreateWindow(title)
        glutDisplayFunc(self.draw)
        glutReshapeFunc(self.reshape)
        glutIdleFunc(self.draw)
        glClearColor(1.0, 1.0, 1.0, 1.0)

    def reshape(self, w, h):
        '''调整图形窗口大小时执行的方法'''
        # 设置视口/视区的左下角x、y坐标以及窗口宽度和高度
        # 视口是最终用来显示图形的区域
        glViewport(0, 0, w, h)
        # 切换到透视投影矩阵
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        # 设置世界窗口的左下角x坐标、右上角x坐标、左下角y坐标、右上角y坐标
        gluOrtho2D(0, w, 0, h)
        # 切换到模型观察矩阵
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()

    def translate2D(self, offset_x, offset_y, vertices):
        '''平移变换'''
        m = identity(3)
        m[0, 2] = offset_x
        m[1, 2] = offset_y
        # 矩阵相乘， 变换矩阵在左侧， 要求每个顶点坐标为列向量
        return m @ vertices

    def rotate2D(self, pivot_point, theta, vertices):
        '''以点pivot_point为中心旋转theta角度'''
        sin_theta = sin(theta)
        cos_theta = cos(theta)
        m = identity(3)
        m[0, 0] = cos_theta
        m[0, 1] = -sin_theta
        m[0, 2] = pivot_point[0] * (1 - cos_theta) + pivot_point[1] * sin_theta
        m[1, 0] = sin_theta
        m[1, 1] = cos_theta
        m[1, 2] = pivot_point[1] * (1 - cos_theta) - pivot_point[0] * sin_theta
        return m @ vertices

    def scale2D(self, sx, sy, origin, vertices):
        '''origin为形状中心，横向缩放比例为sx，纵向缩放比例为sy'''
        m = identity(3)
        m[0, 0] = sx
        m[0, 2] = (1 - sx) * origin[0]
        m[1, 1] = sy
        m[1, 2] = (1 - sy) * origin[1]
        return m @ vertices

    def triangle(self, vertices):
        '''绘制三角形'''
        glBegin(GL_TRIANGLES)
        glVertex2f(*vertices[0][:2])
        glVertex2f(*vertices[1][:2])
        glVertex2f(*vertices[2][:2])
        glEnd()

    def draw(self):
        '''自己的绘图方法，本程序的关键'''
        # 每次绘制图形之前清除颜色缓冲区和深度缓冲区中的信息
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        # 切换到模型观察矩阵，才能正确绘制图形
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()

        # 三角形顶点，使用齐次坐标
        # 这里采用的是行向量形式， 变换矩阵在左侧时需要对顶点坐标矩阵转置
        vertices = array([[150, 250, 1], [250, 250, 1], [230, 400, 1]])
        # 各维度平均值，三角形中心坐标
        center = vertices.mean(axis=0)
        # 绘制原始三角形，蓝色
        glColor3f(0, 0, 1)
        self.triangle(vertices)

        # 以三角形中心为中心进行缩放，然后绘制红色三角形
        vertices = self.scale2D(0.5, 0.5, center, vertices.T).T
        glColor3f(1, 0, 0)
        self.triangle(vertices)

        # 沿三角形中心旋转，然后绘制灰色三角形
        vertices = self.rotate2D(center, radians(90), vertices.T).T
        glColor3f(0.6, 0.6, 0.6)
        self.triangle(vertices)

        # 平移，然后绘制绿色三角形
        vertices = self.translate2D(200, 150, vertices.T).T
        glColor3f(0, 1, 0)
        self.triangle(vertices)

        # 重新定义三角形
        vertices = array([[150, 250, 1], [250, 250, 1], [230, 400, 1]])
        center = vertices.mean(axis=0)

        m = identity(3)
        m = self.translate2D(200, 150,
                             self.rotate2D(center, radians(90),
                                           self.scale2D(0.5, 0.5, center, m)))

        # 正常的话，这里绘制的绿色三角形应该与前面平移的三角形重合
        vertices = (m @ vertices.T).T

        self.triangle((vertices))

        # 交换缓冲区，更新图形
        glutSwapBuffers()

    # 消息主循环
    def mainloop(self):
        glutMainLoop()

if __name__ == '__main__':
    # 创建窗口对象，启动消息主循环
    MyPyOpenGLTest().mainloop()
